package com.github.yeecode.mybatisdemo.reflect;

import com.github.yeecode.mybatisdemo.model.User;
import org.apache.ibatis.reflection.ReflectionException;
import org.apache.ibatis.reflection.Reflector;
import org.apache.ibatis.reflection.factory.ObjectFactory;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : wushikai
 * <p>
 * date : 2021-01-15
 */
public class MyDefaultObjectFactory implements ObjectFactory, Serializable {


    private static final long serialVersionUID = -8855120656740914948L;


    /**
     * 传一个对象的class 进来, 直接创建一个对象, 当然必须符合Java bean规范;
     */
    @Override
    public <T> T create(Class<T> type) {
        return create(type, null, null);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Class<?> classToCreate = resolveInterface(type);
        // we know types are assignable
        return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
    }

    private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
            Constructor<T> constructor;
            if (constructorArgTypes == null || constructorArgs == null) { //如果传进来 的 构造参数类型或者构造实参是空的, 意思就是传进来的是无参构造器;
                constructor = type.getDeclaredConstructor();  //获得构造器
                try {
                    return constructor.newInstance();
                } catch (IllegalAccessException e) {
                    if (Reflector.canControlMemberAccessible()) {//如果构造器是private的, 先把 访问权限设置为 true;
                        constructor.setAccessible(true);
                        return constructor.newInstance();
                    } else {
                        throw e;
                    }
                }
            }
            //一个带参构造器需要参数类型, 需要把list 转成 类型 数组
            constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
            try {
                //当使用  带参构造器 构造 一个 对象 的时候, 必须传递一个 对象数组 其实就是 实参
                return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
            } catch (IllegalAccessException e) {
                if (Reflector.canControlMemberAccessible()) {
                    constructor.setAccessible(true);
                    return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
                } else {
                    throw e;
                }
            }
        } catch (Exception e) {
            //地下看不懂了????????
            String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
                    .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
            String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
                    .stream().map(String::valueOf).collect(Collectors.joining(","));
            throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
        }
    }

    protected static Class<?> resolveInterface(Class<?> type) {
        Class<?> classToCreate;
        if (type == List.class || type == Collection.class || type == Iterable.class) {
            classToCreate = ArrayList.class;
        } else if (type == Map.class) {
            classToCreate = HashMap.class;
        } else if (type == SortedSet.class) { // issue #510 Collections Support
            classToCreate = TreeSet.class;
        } else if (type == Set.class) {
            classToCreate = HashSet.class;
        } else {
            classToCreate = type;
        }
        return classToCreate;
    }

    @Override
    public <T> boolean isCollection(Class<T> type) {
        return Collection.class.isAssignableFrom(type);
    }


    //测试这个类的方法
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        MyDefaultObjectFactory myDefaultObjectFactory = new MyDefaultObjectFactory();
        User user = myDefaultObjectFactory.create(User.class);
        System.out.println(user.toString());

        Constructor<? extends User> declaredConstructor = user.getClass().getDeclaredConstructor();
        System.out.println("declaredConstructor.getName().toString() = " + declaredConstructor.getName().toString());


        // 传一个 有参构造器;
        List<Class<?>> constructorArgTypes = new ArrayList<>();
        //顺序必须和构造器一样, 否则报错;
        constructorArgTypes.add(String.class);
        constructorArgTypes.add(String.class);
        constructorArgTypes.add(String.class);
        constructorArgTypes.add(Integer.class);
        constructorArgTypes.add(Integer.class);
        constructorArgTypes.add(Integer.class);


        Class[] classes = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
        Constructor<? extends User> constructorHasArgs = user.getClass().getDeclaredConstructor(classes);
        User userHasArgs = constructorHasArgs.newInstance("姓名", "校名", "邮箱", 22, 11, 33);
        System.out.println("userHasArgs.toString() = " + userHasArgs.toString());



        test();

    }


    public static void test() {

        ArrayList<String> list = new ArrayList<>();
        list.add("abc1");
        list.add("abc2");
        list.add("abc3");
        list.add("abc4");
        Object[] objects = list.toArray();
        for (Object o : objects) {

            System.out.println(o);
        }

        String[] strings = list.toArray(new String[4]);
        System.out.println(strings[2]);


    }

    public static void test1() {

        Class[] classes = new Class[3];

    }

}


